﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Velocity4Net.Cfg
{
    public class VelocitySettings
       : Hashtable
    {

        private static readonly Byte DEFAULT_BYTE = 0;
        private static readonly Boolean DEFAULT_BOOLEAN = false;
        private static readonly Int16 DEFAULT_INT16 = 0;
        private static readonly Int32 DEFAULT_INT32 = 0;
        private static readonly Single DEFAULT_SINGLE = 0;
        private static readonly Int64 DEFAULT_INT64 = 0;
        private static readonly Double DEFAULT_DOUBLE = 0;

        /// <summary> Default configurations repository.
        /// </summary>
        private VelocitySettings defaults;

        /// <summary> 
        /// The file connected to this repository (holding comments and such).
        /// </summary>
        protected internal String file;

        /// <summary> Base path of the configuration file used to create
        /// this ExtendedProperties object.
        /// </summary>
        protected internal String basePath;

        /// <summary> File separator.
        /// </summary>
        protected internal String fileSeparator = "\\";

        /// <summary> Has this configuration been intialized.
        /// </summary>
        protected internal bool isInitialized = false;

        /// <summary> This is the name of the property that can point to other
        /// properties file for including other properties files.
        /// </summary>
        protected internal static String include = "include";

        /// <summary> These are the keys in the order they listed
        /// in the configuration file. This is useful when
        /// you wish to perform operations with configuration
        /// information in a particular order.
        /// </summary>
        protected internal ArrayList keysAsListed = new ArrayList();

        /// <summary> Creates an empty extended properties object.
        /// </summary>
        public VelocitySettings() : base() { }

        /// <summary> Private initializer method that sets up the generic
        /// resources.
        /// *
        /// </summary>
        /// <exception cref="">IOException, if there was an I/O problem.
        ///
        /// </exception>
        private void Init(VelocitySettings exp)
        {
            isInitialized = true;
        }

        /// <summary> Indicate to client code whether property
        /// resources have been initialized or not.
        /// </summary>
        public virtual bool IsInitialized()
        {
            return isInitialized;
        }

        public virtual String Include
        {
            get { return VelocitySettings.include; }
            set { VelocitySettings.include = value; }
        }

        public virtual IEnumerable Keys
        {
            get { return keysAsListed; }
        }

        /// <summary>  Gets a property from the configuration.
        /// *
        /// </summary>
        /// <param name="key">property to retrieve
        /// </param>
        /// <returns>value as object. Will return user value if exists,
        /// if not then default value if exists, otherwise null
        ///
        /// </returns>
        public virtual Object GetProperty(String key)
        {
            /*
            *  first, try to get from the 'user value' store
            */
            Object o = this[key];

            if (o == null)
            {
                /*
                *  if there isn't a value there, get it from the
                *  defaults if we have them
                */
                if (defaults != null)
                {
                    o = defaults[key];
                }
            }

            return o;
        }

        /// <summary> Add a property to the configuration. If it already
        /// exists then the value stated here will be added
        /// to the configuration entry. For example, if
        /// *
        /// resource.loader = file
        /// *
        /// is already present in the configuration and you
        /// *
        /// addProperty("resource.loader", "classpath")
        /// *
        /// Then you will end up with a Vector like the
        /// following:
        /// *
        /// ["file", "classpath"]
        /// *
        /// </summary>
        /// <param name="String">key
        /// </param>
        /// <param name="String">value
        ///
        /// </param>
        public virtual void AddProperty(String key, Object token)
        {
            Object o = this[key];

            /*
            *  $$$ GMJ
            *  FIXME : post 1.0 release, we need to not assume
            *  that a scalar is a String - it can be an Object
            *  so we should make a little vector-like class
            *  say, Foo that wraps (not extends Vector),
            *  so we can do things like
            *  if ( !( o instanceof Foo) )
            *  so we know it's our 'vector' container
            *
            *  This applies throughout
            */

            if (o is String)
            {
                ArrayList v = new ArrayList(2);
                v.Add(o);
                v.Add(token);
                this[key]= v;
                // Add( key, v);
            }
            else if (o is ArrayList)
            {
                ((ArrayList)o).Add(token);
            }
            else
            {
                /*
                * This is the first time that we have seen
                * request to place an object in the 
                * configuration with the key 'key'. So
                * we just want to place it directly into
                * the configuration ... but we are going to
                * make a special exception for String objects
                * that contain "," characters. We will take
                * CSV lists and turn the list into a vector of
                * Strings before placing it in the configuration.
                * This is a concession for Properties and the
                * like that cannot parse multiple same key
                * values.
                */
                if (token is String && ((String)token).IndexOf(';') > 0)
                {
                    string[] vs = token.ToString().Split(new char[] { ';' });

                    foreach (string s in vs)
                    {
                        //String s = tokenizer.NextToken();

                        /*
                        * we know this is a string, so make sure it
                        * just goes in rather than risking vectorization
                        * if it contains an escaped comma
                        */
                        AddStringProperty(key, s);
                    }
                }
                else
                {
                    /*
                    * We want to keep track of the order the keys
                    * are parsed, or dynamically entered into
                    * the configuration. So when we see a key
                    * for the first time we will place it in
                    * an ArrayList so that if a client class needs
                    * to perform operations with configuration
                    * in a definite order it will be possible.
                    */
                    AddPropertyDirect(key, token);
                }
            }
        }

        /// <summary>   Adds a key/value pair to the map.  This routine does
        /// no magic morphing.  It ensures the keylist is maintained
        /// *
        /// </summary>
        /// <param name="key">key to use for mapping
        /// </param>
        /// <param name="obj">object to store
        ///
        /// </param>
        private void AddPropertyDirect(String key, Object obj)
        {
            /*
            * safety check
            */

            if (!ContainsKey(key))
            {
                keysAsListed.Add(key);
            }

            /*
            * and the value
            */
            this[key]= obj;
        }

        /// <summary>  Sets a string property w/o checking for commas - used
        /// internally when a property has been broken up into
        /// strings that could contain escaped commas to prevent
        /// the inadvertant vectorization.
        ///
        /// Thanks to Leon Messerschmidt for this one.
        ///
        /// </summary>
        private void AddStringProperty(String key, String token)
        {
            Object o = this[key];

            /*
            *  $$$ GMJ
            *  FIXME : post 1.0 release, we need to not assume
            *  that a scalar is a String - it can be an Object
            *  so we should make a little vector-like class
            *  say, Foo that wraps (not extends Vector),
            *  so we can do things like
            *  if ( !( o instanceof Foo) )
            *  so we know it's our 'vector' container
            *
            *  This applies throughout
            */

            /*
            *  do the usual thing - if we have a value and 
            *  it's scalar, make a vector, otherwise add
            *  to the vector
            */

            if (o is String)
            {
                ArrayList v = new ArrayList(2);
                v.Add(o);
                v.Add(token);
                this[key]= v;
            }
            else if (o is ArrayList)
            {
                ((ArrayList)o).Add(token);
            }
            else
            {
                AddPropertyDirect(key, token);
            }
        }

        /// <summary> Set a property, this will replace any previously
        /// set values. Set values is implicitly a call
        /// to clearProperty(key), addProperty(key,value).
        /// </summary>
        /// <param name="String">key
        /// </param>
        /// <param name="String">value
        /// </param>
        public virtual void SetProperty(String key, Object value_Renamed)
        {
            ClearProperty(key);
            AddProperty(key, value_Renamed);
        }


        /// <summary> Combines an existing Hashtable with this Hashtable.
        /// *
        /// Warning: It will overwrite previous entries without warning.
        /// *
        /// </summary>
        /// <param name="">ExtendedProperties
        ///
        /// </param>
        public virtual void Combine(VelocitySettings c)
        {
            IEnumerable i = c.Keys;
            foreach (string key in i)
            {
                SetProperty(key, c[key]);
            }
        }

        /// <summary> Clear a property in the configuration.
        /// *
        /// </summary>
        /// <param name="String">key to remove along with corresponding value.
        ///
        /// </param>
        public virtual void ClearProperty(String key)
        {
            if (ContainsKey(key))
            {
                /*
                        * we also need to rebuild the keysAsListed or else
                        * things get *very* confusing
                        */

                for (int i = 0; i < keysAsListed.Count; i++)
                {
                    if (((String)keysAsListed[i]).Equals(key))
                    {
                        keysAsListed.RemoveAt(i);
                        break;
                    }
                }

                Remove(key);
            }
        }

        /// <summary> Get the list of the keys contained in the configuration
        /// repository.
        /// *
        /// </summary>
        /// <returns>An Iterator.
        ///
        /// </returns>

        /// <summary> Get the list of the keys contained in the configuration
        /// repository that match the specified prefix.
        /// *
        /// </summary>
        /// <param name="prefix">The prefix to test against.
        /// </param>
        /// <returns>An Iterator of keys that match the prefix.
        ///
        /// </returns>
        public virtual IEnumerable GetKeys(String prefix)
        {
            IEnumerable keys = Keys;
            ArrayList matchingKeys = new ArrayList();

            foreach (Object key in keys)
            {
                if (key is String && ((String)key).StartsWith(prefix))
                {
                    matchingKeys.Add(key);
                }
            }
            return matchingKeys;
        }

        /// <summary> Create an ExtendedProperties object that is a subset
        /// of this one. Take into account duplicate keys
        /// by using the setProperty() in ExtendedProperties.
        /// *
        /// </summary>
        /// <param name="String">prefix
        ///
        /// </param>
        public virtual VelocitySettings Subset(String prefix)
        {
            VelocitySettings c = new VelocitySettings();
            IEnumerable keys = Keys;
            bool validSubset = false;

            foreach (Object key in keys)
            {
                if (key is String && ((String)key).StartsWith(prefix))
                {
                    if (!validSubset)
                    {
                        validSubset = true;
                    }

                    String newKey = null;

                    /*
                    * Check to make sure that c.subset(prefix) doesn't
                    * blow up when there is only a single property
                    * with the key prefix. This is not a useful
                    * subset but it is a valid subset.
                    */
                    if (((String)key).Length == prefix.Length)
                    {
                        newKey = prefix;
                    }
                    else
                    {
                        newKey = ((String)key).Substring(prefix.Length + 1);
                    }

                    /*
                    *  use addPropertyDirect() - this will plug the data as 
                    *  is into the Map, but will also do the right thing
                    *  re key accounting
                    */

                    c.AddPropertyDirect(newKey, this[key]);
                }
            }

            if (validSubset)
            {
                return c;
            }
            else
            {
                return null;
            }
        }

        /// <summary> Display the configuration for debugging
        /// purposes.
        /// </summary>
        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            IEnumerable i = Keys;

            foreach (Object key in i)
            {
                Object value = this[key];
                sb.Append(key + " => " + ValueToString(value)).Append(Environment.NewLine);
            }
            return sb.ToString();
        }

        private String ValueToString(Object value)
        {
            if (value is ArrayList)
            {
                String s = "ArrayList :: ";
                foreach (Object o in (ArrayList)value)
                {
                    if (!s.EndsWith(", "))
                    {
                        s += ", ";
                    }
                    s += "[" + o.ToString() + "]";
                }
                return s;
            }
            else
            {
                return value.ToString();
            }
        }

        /// <summary> Get a string associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated string.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a String.
        ///
        /// </exception>
        public virtual String GetString(String key)
        {
            return GetString(key, null);
        }

        /// <summary> Get a string associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated string if key is found,
        /// default value otherwise.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a String.
        ///
        /// </exception>
        public virtual String GetString(String key, String defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is String)
            {
                return (String)value_Renamed;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetString(key, defaultValue);
                }
                else
                {
                    return defaultValue;
                }
            }
            else if (value_Renamed is ArrayList)
            {
                //UPGRADE_TODO: Method java.util.Vector.get was not converted. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1095"'
                return (String)((ArrayList)value_Renamed)[0];
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a String object");
            }
        }

        /// <summary> Get a list of properties associated with the given
        /// configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated properties if key is found.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a String/Vector.
        /// </exception>
        /// <exception cref="">IllegalArgumentException if one of the tokens is
        /// malformed (does not contain an equals sign).
        ///
        /// </exception>
        public virtual Hashtable GetProperties(String key)
        {
            //UPGRADE_TODO: Format of property file may need to be changed. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1089"'
            return GetProperties(key, new Hashtable());
        }

        /// <summary> Get a list of properties associated with the given
        /// configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated properties if key is found.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a String/Vector.
        /// </exception>
        /// <exception cref="">IllegalArgumentException if one of the tokens is
        /// malformed (does not contain an equals sign).
        ///
        /// </exception>

        // NOTE: java use java.util.Properties - the conversion used Configuration.AppSettingsReader - i am going to convert to hashtable
        public virtual Hashtable GetProperties(String key, Hashtable defaults)
        {
            /*
            * Grab an array of the tokens for this key.
            */
            String[] tokens = GetStringArray(key);

            /*
            * Each token is of the form 'key=value'.
            */
            //UPGRADE_TODO: Format of property file may need to be changed. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1089"'
            Hashtable props = new Hashtable(defaults);
            for (int i = 0; i < tokens.Length; i++)
            {
                String token = tokens[i];
                int equalSign = token.IndexOf((Char)'=');
                if (equalSign > 0)
                {
                    String pkey = token.Substring(0, (equalSign) - (0)).Trim();
                    String pvalue = token.Substring(equalSign + 1).Trim();
                    props.Add(pkey, pvalue);
                }
                else
                {
                    throw new ArgumentException('\'' + token + "' does not contain " + "an equals sign");
                }
            }
            return props;
        }

        /// <summary> Get an array of strings associated with the given configuration
        /// key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated string array if key is found.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a String/Vector.
        ///
        /// </exception>
        public virtual String[] GetStringArray(String key)
        {
            Object value_Renamed = this[key];

            // What's your vector, Victor?
            ArrayList vector;
            if (value_Renamed is String)
            {
                vector = new ArrayList(1);
                vector.Add(value_Renamed);
            }
            else if (value_Renamed is ArrayList)
            {
                vector = (ArrayList)value_Renamed;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetStringArray(key);
                }
                else
                {
                    return new String[0];
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a String/Vector object");
            }

            String[] tokens = new String[vector.Count];
            for (int i = 0; i < tokens.Length; i++)
            {
                tokens[i] = (String)vector[i];
            }

            return tokens;
        }

        /// <summary> Get a Vector of strings associated with the given configuration
        /// key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated Vector.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Vector.
        ///
        /// </exception>
        public virtual List<object> GetVector(String key)
        {
            return GetVector(key, null);
        }

        /// <summary> Get a Vector of strings associated with the given configuration
        /// key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated Vector.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Vector.
        ///
        /// </exception>
        public virtual List<object> GetVector(String key, List<object> defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is List<object>)
            {
                return (List<object>)value_Renamed;
            }
            else if (value_Renamed is String)
            {
                List<object> v = new List<object>(1);
                v.Add((String)value_Renamed);
                this[key]= v;
                return v;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetVector(key, defaultValue);
                }
                else
                {
                    return ((defaultValue == null) ? new List<object>() : defaultValue);
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a Vector object");
            }
        }

        /// <summary> Get a boolean associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated boolean.
        /// </returns>
        /// <exception cref="">NoSuchElementException is thrown if the key doesn't
        /// map to an existing object.
        /// </exception>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Boolean.
        ///
        /// </exception>
        public virtual bool GetBoolean(String key)
        {
            Boolean b = GetBoolean(key, DEFAULT_BOOLEAN);
            if ((Object)b != null)
            {
                return b;
            }
            else
            {
                throw new Exception('\'' + key + "' doesn't map to an existing object");
            }
        }

        /// <summary> Get a boolean associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated boolean if key is found and has valid
        /// format, default value otherwise.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Boolean.
        ///
        /// </exception>
        public virtual Boolean GetBoolean(String key, Boolean defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is Boolean)
            {
                return (Boolean)value_Renamed;
            }
            else if (value_Renamed is String)
            {
                String s = TestBoolean((String)value_Renamed);
                Boolean b = s.ToUpper().Equals("TRUE");
                this[key]= b;
                return b;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetBoolean(key, defaultValue);
                }
                else
                {
                    return defaultValue;
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a Boolean object");
            }
        }

        /// <summary> Test whether the string represent by value maps to a boolean
        /// value or not. We will allow <code>true</code>, <code>on</code>,
        /// and <code>yes</code> for a <code>true</code> boolean value, and
        /// <code>false</code>, <code>off</code>, and <code>no</code> for
        /// <code>false</code> boolean values.  Case of value to test for
        /// boolean status is ignored.
        /// *
        /// </summary>
        /// <param name="String">The value to test for boolean state.
        /// </param>
        /// <returns><code>true</code> or <code>false</code> if the supplied
        /// text maps to a boolean value, or <code>null</code> otherwise.
        ///
        /// </returns>
        public virtual String TestBoolean(String value_Renamed)
        {
            String s = ((String)value_Renamed).ToLower();

            if (s.Equals("true") || s.Equals("on") || s.Equals("yes"))
            {
                return "true";
            }
            else if (s.Equals("false") || s.Equals("off") || s.Equals("no"))
            {
                return "false";
            }
            else
            {
                return null;
            }
        }

        /// <summary> Get a byte associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated byte.
        /// </returns>
        /// <exception cref="">NoSuchElementException is thrown if the key doesn't
        /// map to an existing object.
        /// </exception>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Byte.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual sbyte GetByte(String key)
        {
            if (ContainsKey(key))
            {
                Byte b = GetByte(key, DEFAULT_BYTE);
                return (sbyte)b;
            }
            else
            {
                throw new Exception('\'' + key + " doesn't map to an existing object");
            }
        }

        /// <summary> Get a byte associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated byte.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Byte.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual sbyte GetByte(String key, sbyte defaultValue)
        {
            return (sbyte)GetByte(key, defaultValue);
        }

        /// <summary> Get a byte associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated byte if key is found and has valid
        /// format, default value otherwise.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Byte.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual Byte GetByte(String key, Byte defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is Byte)
            {
                return (Byte)value_Renamed;
            }
            else if (value_Renamed is String)
            {
                Byte b = Byte.Parse((String)value_Renamed);
                this[key]= b;
                return b;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetByte(key, defaultValue);
                }
                else
                {
                    return defaultValue;
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a Byte object");
            }
        }

        /// <summary> Get a short associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated short.
        /// </returns>
        /// <exception cref="">NoSuchElementException is thrown if the key doesn't
        /// map to an existing object.
        /// </exception>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Short.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual short GetShort(String key)
        {
            Int16 s = GetShort(key, DEFAULT_INT16);
            if ((Object)s != null)
            {
                return (short)s;
            }
            else
            {
                throw new Exception('\'' + key + "' doesn't map to an existing object");
            }
        }

        /// <summary> Get a short associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated short if key is found and has valid
        /// format, default value otherwise.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Short.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual Int16 GetShort(String key, Int16 defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is Int16)
            {
                return (Int16)value_Renamed;
            }
            else if (value_Renamed is String)
            {
                Int16 s = Int16.Parse((String)value_Renamed);
                this[key]= s;
                return s;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetShort(key, defaultValue);
                }
                else
                {
                    return defaultValue;
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a Short object");
            }
        }

        /// <summary> The purpose of this method is to get the configuration resource
        /// with the given name as an integer.
        /// *
        /// </summary>
        /// <param name="name">The resource name.
        /// </param>
        /// <returns>The value of the resource as an integer.
        ///
        /// </returns>
        public virtual int GetInt(String name)
        {
            return GetInteger(name);
        }

        /// <summary> The purpose of this method is to get the configuration resource
        /// with the given name as an integer, or a default value.
        /// *
        /// </summary>
        /// <param name="name">The resource name
        /// </param>
        /// <param name="def">The default value of the resource.
        /// </param>
        /// <returns>The value of the resource as an integer.
        ///
        /// </returns>
        public virtual int GetInt(String name, int def)
        {
            return GetInteger(name, def);
        }

        /// <summary> Get a int associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated int.
        /// </returns>
        /// <exception cref="">NoSuchElementException is thrown if the key doesn't
        /// map to an existing object.
        /// </exception>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Integer.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual int GetInteger(String key)
        {
            Int32 i = GetInteger(key, DEFAULT_INT32);
            if ((Object)i != null)
            {
                return i;
            }
            else
            {
                throw new Exception('\'' + key + "' doesn't map to an existing object");
            }
        }

        /// <summary> Get a int associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated int if key is found and has valid
        /// format, default value otherwise.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Integer.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual Int32 GetInteger(String key, Int32 defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is Int32)
            {
                return (Int32)value_Renamed;
            }
            else if (value_Renamed is String)
            {
                Int32 i = Int32.Parse((String)value_Renamed);
                this[key]= i;
                return i;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetInteger(key, defaultValue);
                }
                else
                {
                    return defaultValue;
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a Integer object");
            }
        }

        /// <summary> Get a long associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated long.
        /// </returns>
        /// <exception cref="">NoSuchElementException is thrown if the key doesn't
        /// map to an existing object.
        /// </exception>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Long.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual long GetLong(String key)
        {
            Int64 l = GetLong(key, DEFAULT_INT64);
            if ((Object)l != null)
            {
                return (long)l;
            }
            else
            {
                throw new Exception('\'' + key + "' doesn't map to an existing object");
            }
        }

        /// <summary> Get a long associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated long if key is found and has valid
        /// format, default value otherwise.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Long.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual Int64 GetLong(String key, Int64 defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is Int64)
            {
                return (Int64)value_Renamed;
            }
            else if (value_Renamed is String)
            {
                Int64 l = Int64.Parse((String)value_Renamed);
                this[key]= l;
                return l;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetLong(key, defaultValue);
                }
                else
                {
                    return defaultValue;
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a Long object");
            }
        }

        /// <summary> Get a float associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated float.
        /// </returns>
        /// <exception cref="">NoSuchElementException is thrown if the key doesn't
        /// map to an existing object.
        /// </exception>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Float.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual float GetFloat(String key)
        {
            Single f = GetFloat(key, DEFAULT_SINGLE);
            if ((Object)f != null)
            {
                return (float)f;
            }
            else
            {
                throw new Exception('\'' + key + "' doesn't map to an existing object");
            }
        }

        /// <summary> Get a float associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated float if key is found and has valid
        /// format, default value otherwise.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Float.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual Single GetFloat(String key, Single defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is Single)
            {
                return (Single)value_Renamed;
            }
            else if (value_Renamed is String)
            {
                //UPGRADE_TODO: Format of parameters of constructor 'java.lang.Float.Float' are different in the equivalent in .NET. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1092"'
                Single f = Single.Parse((String)value_Renamed);
                this[key]= f;
                return f;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetFloat(key, defaultValue);
                }
                else
                {
                    return defaultValue;
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a Float object");
            }
        }

        /// <summary> Get a double associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <returns>The associated double.
        /// </returns>
        /// <exception cref="">NoSuchElementException is thrown if the key doesn't
        /// map to an existing object.
        /// </exception>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Double.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual double GetDouble(String key)
        {
            Double d = GetDouble(key, DEFAULT_DOUBLE);
            if ((Object)d != null)
            {
                return d;
            }
            else
            {
                throw new Exception('\'' + key + "' doesn't map to an existing object");
            }
        }

        /// <summary> Get a double associated with the given configuration key.
        /// *
        /// </summary>
        /// <param name="key">The configuration key.
        /// </param>
        /// <param name="defaultValue">The default value.
        /// </param>
        /// <returns>The associated double if key is found and has valid
        /// format, default value otherwise.
        /// </returns>
        /// <exception cref="">ClassCastException is thrown if the key maps to an
        /// object that is not a Double.
        /// </exception>
        /// <exception cref="">NumberFormatException is thrown if the value mapped
        /// by the key has not a valid number format.
        ///
        /// </exception>
        public virtual Double GetDouble(String key, Double defaultValue)
        {
            Object value_Renamed = this[key];

            if (value_Renamed is Double)
            {
                return (Double)value_Renamed;
            }
            else if (value_Renamed is String)
            {
                //UPGRADE_TODO: Format of parameters of constructor 'java.lang.Double.Double' are different in the equivalent in .NET. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1092"'
                Double d = Double.Parse((String)value_Renamed);
                this[key]= d;
                return d;
            }
            else if (value_Renamed == null)
            {
                if (defaults != null)
                {
                    return defaults.GetDouble(key, defaultValue);
                }
                else
                {
                    return defaultValue;
                }
            }
            else
            {
                throw new InvalidCastException('\'' + key + "' doesn't map to a Double object");
            }
        }

        /// <summary> Convert a standard properties class into a configuration
        /// class.
        /// *
        /// </summary>
        /// <param name="p">properties object to convert into
        /// a ExtendedProperties object.
        /// *
        /// </param>
        /// <returns>ExtendedProperties configuration created from the
        /// properties object.
        ///
        /// </returns>

        public static VelocitySettings ConvertProperties(VelocitySettings p)
        {
            VelocitySettings c = new VelocitySettings();

            for (IEnumerator e = (IEnumerator)p.Keys; e.MoveNext(); )
            {
                String key = (String)e.Current;
                String value = p.GetProperty(key).ToString();
                c.SetProperty(key, value);
            }

            return c;
        }
    }
}
